﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;
using System.IO;

namespace NFit.WordML
{
    public class WordDocumentParser
    {
        private ILogger logger;
        private Stream stream;
        private string fileName;
        private WordFileMapHierarchy refHierarchy;

        public WordDocumentParser(string wordfilename, WordFileMapHierarchy hierarchy, ILogger logger)
        {
            fileName = wordfilename;
            this.logger = logger;
            this.refHierarchy = hierarchy;
        }

        public WordDocumentParser(Stream wordfilestream, WordFileMapHierarchy hierarchy, ILogger logger)
        {
            stream = wordfilestream;
            this.logger = logger;
            this.refHierarchy = hierarchy;
        }

        public WordElementContainer Parse()
        {
            if (!string.IsNullOrEmpty(fileName) && File.Exists(fileName))
            {
                Log("Parsing file \"" + fileName + "\"");
                using (WordprocessingDocument myDoc = WordprocessingDocument.Open(fileName, false))
                {
                    return ParseDocument(myDoc);
                }
            }

            if (stream != null)
            {
                Log("Parsing file \"" + fileName + "\"");
                using (WordprocessingDocument myDoc = WordprocessingDocument.Open(stream, false))
                {
                    return ParseDocument(myDoc);
                }
            }

            return null;
        }

        private WordElementContainer ParseDocument(WordprocessingDocument myDoc)
        {
            MainDocumentPart mainPart = myDoc.MainDocumentPart;
            var styles = WordStyleExtensions.GetStyleIdAndName(mainPart);
            var source = mainPart.Document.Body;
            var wrkHierarchy = ReadHierarchy(mainPart, refHierarchy);
            LogHierarchy(mainPart, wrkHierarchy, 0);
            WordElementContainer rootElt = new WordElementContainer(mainPart.Document.Body, null);
            WordElementContainer currentElt = rootElt;

            foreach (var child in source.ChildElements)
            {
                string childtext = child.InnerText;
                WordFileMapHierarchy childHierarchy = null;
                if (child is Paragraph)
                {
                    var pStyle = (child as Paragraph).StyleId();
                    childHierarchy = wrkHierarchy.GetChildStyle(pStyle);
                    if (childHierarchy != null)
                    {
                        if (childHierarchy.IsSameOrParentOf(currentElt.Style))
                        {
                            currentElt = currentElt.Parent;
                            while (currentElt.Parent != null && !currentElt.Style.IsParentOf(childHierarchy))
                                currentElt = currentElt.Parent;

                            currentElt = currentElt.Add(child, childHierarchy);
                            continue;
                        }

                        if (currentElt.Style == null || childHierarchy.IsChildOf(currentElt.Style))
                        {
                            currentElt = currentElt.Add(child, childHierarchy);
                            continue;
                        }
                    }
                }
                childtext += "test";
                var elt = currentElt.Add(child, childHierarchy);                    
            }
            Log("########## Elements");
            LogElements(mainPart, rootElt, 0);
            return rootElt;
        }

        private void LogElements(MainDocumentPart mainPart, WordElementContainer elt, int idx)
        {
            if (!(elt.Element is Body))
            {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < idx; i++)
                {
                    sb.Append("\t");
                }
                LogElement(mainPart, sb.ToString(), elt);
            }
            else
            {
                idx--;
            }

            foreach(var child in elt)
                LogElements(mainPart, child, idx + 1);
                    
        }

        private void LogElement(MainDocumentPart mainPart, string prefix, WordElementContainer elt)
        {
            if (elt.Element is Paragraph)
            {
                Paragraph para = elt.Element as Paragraph;
                string styleId = para.StyleId();
                Log(prefix + styleId + " (" + WordStyleExtensions.GetStyleNameFromStyleId(mainPart, styleId) + ") : " + para.InnerText);
                return;
            }
            else if (elt.Element is Table)
            {
                Table tbl = elt.Element as Table;
                TableProperties tblPr = tbl.GetFirstChild<TableProperties>();
                string tablestyle = tblPr.TableStyle.Val ?? string.Empty;
                Log(prefix + "une table avec " + tbl.Descendants<TableRow>().Count() + " lignes (" + tablestyle + ")");
                foreach (var row in tbl.Descendants<TableRow>())
                {
                    StringBuilder sb = new StringBuilder();
                    sb.Append(prefix);
                    foreach(var cell in row.Descendants<TableCell>())
                    {
                        var span = cell.TableCellProperties.GridSpan;
                        if (span != null)
                            sb.Append("#SPAN" + span.Val + "#");
                        var vMerge = cell.TableCellProperties.VerticalMerge;
                        if (vMerge != null)
                            sb.Append("#MERGE" + vMerge.Val + "#");
                        sb.Append(cell.InnerText);
                        sb.Append("\t");
                    } 
                    Log(sb.ToString());
                }
                return;
            }

            Log(prefix + elt.Element.GetType().Name + " : " + elt.Element.InnerText);
        }

        private void LogHierarchy(MainDocumentPart mainPart, WordFileMapHierarchy hierarchy, int idx)
        {
            if (hierarchy.StyleName != null)
            {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < idx; i++)
                {
                    sb.Append("\t");
                }

                sb.Append("Number of paragraphs with \""
                          + hierarchy.StyleName + "\" styles: "
                          + mainPart.ParagraphsByStyleName(hierarchy.StyleName)
                                .Count());
                Log(sb.ToString());
            }
            else
            {
                idx--;
            }
            if (hierarchy.Childs != null)
            {
                foreach (var child in hierarchy.Childs)
                    LogHierarchy(mainPart, child, idx + 1);
            }
        }

        private WordFileMapHierarchy ReadHierarchy(MainDocumentPart mainPart, WordFileMapHierarchy rootHierarchy)
        {
            WordFileMapHierarchy res;
            if (!string.IsNullOrEmpty(rootHierarchy.StyleName))
            {                 
                string styleId = WordStyleExtensions.GetStyleIdFromStyleName(mainPart, rootHierarchy.StyleName);
                res = new WordFileMapHierarchy(rootHierarchy.StyleName, styleId);
            }
            else
            {
                res = new WordFileMapHierarchy(rootHierarchy.StyleName);
            }

            foreach(var child in rootHierarchy.Childs)
            {
                res.Add(ReadHierarchy(mainPart, child));    
            }
            return res;
        }

        private void Log(string text)
        {
            if (logger != null)
                logger.Log(text);
        }
    }
}